﻿using System;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text;
using System.Globalization;
using System.ComponentModel;
using System.Security;
using System.Security.Cryptography;

using Microsoft.SqlServer.Server;

namespace PickGold.SqlServer
{
	/// <summary>
	/// Security
	/// </summary>
	public static class Security
	{
		private static byte[] FillKey(byte[] data, int size)
		{
			size /= 8;
			if (data.Length == size)
				return data;

			var bs = new byte[size];
			var dl = data.Length;
			if (dl > size)
			{
				Array.Copy(data, 0, bs, 0, size);
				return bs;
			}

			for (var i = 0; i < size; i += dl)
				Array.Copy(data, 0, bs, i, size - i - dl > 0 ? dl : size - i);
			return bs;
		}

		/// <summary>
		/// Aes Encode
		/// </summary>
		/// <param name="input">input</param>
		/// <param name="key">key</param>
		/// <param name="iv">iv</param>
		/// <returns>return</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("AES 对称加密")]
		public static SqlBinary AesEncode([Description("要加密的数据")]SqlBinary input, [Description("加密密钥")]SqlBinary key, [Description("加密向量")]SqlBinary iv)
		{
			if (input.IsNull || input.Value == null || input.Length == 0)
				return null;

			if ((key.IsNull || key.Value == null || key.Value.Length == 0) &&
				(iv.IsNull || iv.Value == null || iv.Value.Length == 0))
				return null;

			var aes = RijndaelManaged.Create();
			if (key.IsNull || key.Value == null || key.Value.Length == 0)
			{
				var bs = iv.Value;
				key = iv;
				if (bs.Length >= aes.KeySize + aes.KeySize / 2)
				{
					var vs = new byte[aes.KeySize];
					Array.Copy(bs, 0, vs, 0, vs.Length);
					aes.Key = vs;
					vs = new byte[aes.KeySize / 2];
					Array.Copy(bs, aes.KeySize, vs, 0, vs.Length);
					aes.IV = vs;
				}
				else
				{
					aes.Key = FillKey(key.Value, aes.KeySize);
					aes.IV = FillKey(iv.Value, aes.KeySize / 2);
				}
			}
			else if (iv.IsNull || iv.Value == null || iv.Value.Length == 0)
			{
				var bs = key.Value;
				iv = key;
				if (bs.Length >= aes.KeySize + aes.KeySize / 2)
				{
					var vs = new byte[aes.KeySize];
					Array.Copy(bs, 0, vs, 0, vs.Length);
					aes.Key = vs;
					vs = new byte[aes.KeySize / 2];
					Array.Copy(bs, aes.KeySize, vs, 0, vs.Length);
					aes.IV = vs;
				}
				else
				{
					aes.Key = FillKey(key.Value, aes.KeySize);
					aes.IV = FillKey(iv.Value, aes.KeySize / 2);
				}
			}
			else
			{
				aes.Key = FillKey(key.Value, aes.KeySize);
				aes.IV = FillKey(iv.Value, aes.KeySize / 2);
			}
			using (var ms = new MemoryStream())
			{
				using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
				{
					var bs = input.Value;
					cs.Write(bs, 0, bs.Length);
					cs.FlushFinalBlock();
					cs.Close();
					bs = ms.ToArray();
					input = new SqlBinary(bs);
				}
				ms.Close();
			}
			aes.Clear();
			return input;
		}

		/// <summary>
		/// Aes Decode
		/// </summary>
		/// <param name="input">input</param>
		/// <param name="key">key</param>
		/// <param name="iv">iv</param>
		/// <returns>return</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("AES 对称解密")]
		public static SqlBinary AesDecode([Description("要解密的数据")]SqlBinary input, [Description("解密密钥")]SqlBinary key, [Description("解密向量")]SqlBinary iv)
		{
			if (input.IsNull || input.Value == null || input.Length == 0)
				return null;

			if ((key.IsNull || key.Value == null || key.Value.Length == 0) &&
				(iv.IsNull || iv.Value == null || iv.Value.Length == 0))
				return null;

			var aes = RijndaelManaged.Create();
			if (key.IsNull || key.Value == null || key.Value.Length == 0)
			{
				var bs = iv.Value;
				key = iv;
				if (bs.Length >= aes.KeySize + aes.KeySize / 2)
				{
					var vs = new byte[aes.KeySize];
					Array.Copy(bs, 0, vs, 0, vs.Length);
					aes.Key = vs;
					vs = new byte[aes.KeySize / 2];
					Array.Copy(bs, aes.KeySize, vs, 0, vs.Length);
					aes.IV = vs;
				}
				else
				{
					aes.Key = FillKey(key.Value, aes.KeySize);
					aes.IV = FillKey(iv.Value, aes.KeySize / 2);
				}
			}
			else if (iv.IsNull || iv.Value == null || iv.Value.Length == 0)
			{
				var bs = key.Value;
				iv = key;
				if (bs.Length >= aes.KeySize + aes.KeySize / 2)
				{
					var vs = new byte[aes.KeySize];
					Array.Copy(bs, 0, vs, 0, vs.Length);
					aes.Key = vs;
					vs = new byte[aes.KeySize / 2];
					Array.Copy(bs, aes.KeySize, vs, 0, vs.Length);
					aes.IV = vs;
				}
				else
				{
					aes.Key = FillKey(key.Value, aes.KeySize);
					aes.IV = FillKey(iv.Value, aes.KeySize / 2);
				}
			}
			else
			{
				aes.Key = FillKey(key.Value, aes.KeySize);
				aes.IV = FillKey(iv.Value, aes.KeySize / 2);
			}
			using (var ms = new MemoryStream(input.Value))
			{
				using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read))
				{
					using (var rs = new MemoryStream())
					{
						var bs = input.Value;
						while (true)
						{
							var i = cs.Read(bs, 0, bs.Length);
							if (i == 0)
								break;

							rs.Write(bs, 0, i);
						}
						bs = rs.ToArray();
						rs.Close();
						input = new SqlBinary(bs);
					}
					cs.Close();
				}
				ms.Close();
			}
			aes.Clear();
			return input;
		}

		/// <summary>
		/// Des Encode
		/// </summary>
		/// <param name="input">input</param>
		/// <param name="key">key</param>
		/// <param name="iv">iv</param>
		/// <returns>return</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("DES 对称加密")]
		public static SqlBinary DesEncode([Description("要加密的数据")]SqlBinary input, [Description("加密密钥")]SqlBinary key, [Description("加密向量")]SqlBinary iv)
		{
			if (input.IsNull || input.Value == null || input.Length == 0)
				return null;

			if ((key.IsNull || key.Value == null || key.Value.Length == 0) &&
				(iv.IsNull || iv.Value == null || iv.Value.Length == 0))
				return null;

			var des = new DESCryptoServiceProvider();
			if (key.IsNull || key.Value == null || key.Value.Length == 0)
			{
				var bs = iv.Value;
				key = iv;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else if (iv.IsNull || iv.Value == null || iv.Value.Length == 0)
			{
				var bs = key.Value;
				iv = key;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else
			{
				des.Key = FillKey(key.Value, des.KeySize);
				des.IV = FillKey(iv.Value, des.KeySize);
			}
			using (var ms = new MemoryStream())
			{
				using (var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
				{
					var bs = input.Value;
					cs.Write(bs, 0, bs.Length);
					cs.FlushFinalBlock();
					cs.Close();
					bs = ms.ToArray();
					input = new SqlBinary(bs);
				}
				ms.Close();
			}
			des.Clear();
			return input;
		}

		/// <summary>
		/// Des Decode
		/// </summary>
		/// <param name="input">input</param>
		/// <param name="key">key</param>
		/// <param name="iv">iv</param>
		/// <returns>return</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("DES 对称解密")]
		public static SqlBinary DesDecode([Description("要解密的数据")]SqlBinary input, [Description("解密密钥")]SqlBinary key, [Description("解密向量")]SqlBinary iv)
		{
			if (input.IsNull || input.Value == null || input.Length == 0)
				return null;

			if ((key.IsNull || key.Value == null || key.Value.Length == 0) &&
				(iv.IsNull || iv.Value == null || iv.Value.Length == 0))
				return null;

			var des = new DESCryptoServiceProvider();
			if (key.IsNull || key.Value == null || key.Value.Length == 0)
			{
				var bs = iv.Value;
				key = iv;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else if (iv.IsNull || iv.Value == null || iv.Value.Length == 0)
			{
				var bs = key.Value;
				iv = key;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else
			{
				des.Key = FillKey(key.Value, des.KeySize);
				des.IV = FillKey(iv.Value, des.KeySize);
			}
			using (var ms = new MemoryStream(input.Value))
			{
				using (var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read))
				{
					using (var rs = new MemoryStream())
					{
						var bs = input.Value;
						while (true)
						{
							var i = cs.Read(bs, 0, bs.Length);
							if (i == 0)
								break;

							rs.Write(bs, 0, i);
						}
						bs = rs.ToArray();
						rs.Close();
						input = new SqlBinary(bs);
					}
					cs.Close();
				}
				ms.Close();
			}
			des.Clear();
			return input;
		}

		/// <summary>
		/// Tdes Encode
		/// </summary>
		/// <param name="input">input</param>
		/// <param name="key">key</param>
		/// <param name="iv">iv</param>
		/// <returns>return</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("TDES 对称加密")]
		public static SqlBinary TdesEncode([Description("要加密的数据")]SqlBinary input, [Description("加密密钥")]SqlBinary key, [Description("加密向量")]SqlBinary iv)
		{
			if (input.IsNull || input.Value == null || input.Length == 0)
				return null;

			if ((key.IsNull || key.Value == null || key.Value.Length == 0) &&
				(iv.IsNull || iv.Value == null || iv.Value.Length == 0))
				return null;

			var des = new DESCryptoServiceProvider();
			if (key.IsNull || key.Value == null || key.Value.Length == 0)
			{
				var bs = iv.Value;
				key = iv;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else if (iv.IsNull || iv.Value == null || iv.Value.Length == 0)
			{
				var bs = key.Value;
				iv = key;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else
			{
				des.Key = FillKey(key.Value, des.KeySize);
				des.IV = FillKey(iv.Value, des.KeySize);
			}
			using (var ms = new MemoryStream())
			{
				using (var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
				{
					var bs = input.Value;
					cs.Write(bs, 0, bs.Length);
					cs.FlushFinalBlock();
					cs.Close();
					bs = ms.ToArray();
					input = new SqlBinary(bs);
				}
				ms.Close();
			}
			des.Clear();
			return input;
		}

		/// <summary>
		/// Tdes Decode
		/// </summary>
		/// <param name="input">input</param>
		/// <param name="key">key</param>
		/// <param name="iv">iv</param>
		/// <returns>return</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("TDES 对称解密")]
		public static SqlBinary TdesDecode([Description("要解密的数据")]SqlBinary input, [Description("解密密钥")]SqlBinary key, [Description("解密向量")]SqlBinary iv)
		{
			if (input.IsNull || input.Value == null || input.Length == 0)
				return null;

			if ((key.IsNull || key.Value == null || key.Value.Length == 0) &&
				(iv.IsNull || iv.Value == null || iv.Value.Length == 0))
				return null;

			var des = new DESCryptoServiceProvider();
			if (key.IsNull || key.Value == null || key.Value.Length == 0)
			{
				var bs = iv.Value;
				key = iv;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else if (iv.IsNull || iv.Value == null || iv.Value.Length == 0)
			{
				var bs = key.Value;
				iv = key;
				if (bs.Length >= des.KeySize * 2)
				{
					des.Key = new byte[] { bs[0x0], bs[0x2], bs[0x4], bs[0x6], bs[0x8], bs[0xA], bs[0xC], bs[0xE] };
					des.IV = new byte[] { bs[0x1], bs[0x3], bs[0x5], bs[0x7], bs[0x9], bs[0xB], bs[0xD], bs[0xF] };
				}
				else
				{
					des.Key = FillKey(key.Value, des.KeySize);
					des.IV = FillKey(iv.Value, des.KeySize);
				}
			}
			else
			{
				des.Key = FillKey(key.Value, des.KeySize);
				des.IV = FillKey(iv.Value, des.KeySize);
			}
			using (var ms = new MemoryStream(input.Value))
			{
				using (var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Read))
				{
					using (var rs = new MemoryStream())
					{
						var bs = input.Value;
						while (true)
						{
							var i = cs.Read(bs, 0, bs.Length);
							if (i == 0)
								break;

							rs.Write(bs, 0, i);
						}
						bs = rs.ToArray();
						rs.Close();
						input = new SqlBinary(bs);
					}
					cs.Close();
				}
				ms.Close();
			}
			des.Clear();
			return input;
		}

		/// <summary>
		/// Md5 Crypto
		/// </summary>
		/// <param name="data">data</param>
		/// <returns>MD5</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("MD5 单向哈希加密")]
		public static SqlBinary Md5Crypto([Description("要加密的数据")]SqlBinary data)
		{
			if (data.IsNull || data.Value == null)
				data = new SqlBinary(new byte[] { });

			var md5 = MD5.Create();
			data = new SqlBinary(md5.ComputeHash(data.Value));
			md5.Clear();
			return data;
		}

		/// <summary>
		/// Sha1 Crypto
		/// </summary>
		/// <param name="data">data</param>
		/// <returns>Sha1</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("SHA1 单向哈希加密")]
		public static SqlBinary Sha1Crypto([Description("要加密的数据")]SqlBinary data)
		{
			if (data.IsNull || data.Value == null)
				data = new SqlBinary(new byte[] { });

			var sha = SHA1.Create();
			data = new SqlBinary(sha.ComputeHash(data.Value));
			sha.Clear();
			return data;
		}

		/// <summary>
		/// Sha256 Crypto
		/// </summary>
		/// <param name="data">data</param>
		/// <returns>Sha256</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("SHA256 单向哈希加密")]
		public static SqlBinary Sha256Crypto([Description("要加密的数据")]SqlBinary data)
		{
			if (data.IsNull || data.Value == null)
				data = new SqlBinary(new byte[] { });

			var sha = SHA256.Create();
			data = new SqlBinary(sha.ComputeHash(data.Value));
			sha.Clear();
			return data;
		}

		/// <summary>
		/// Sha384 Crypto
		/// </summary>
		/// <param name="data">data</param>
		/// <returns>Sha384</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("SHA384 单向哈希加密")]
		public static SqlBinary Sha384Crypto([Description("要加密的数据")]SqlBinary data)
		{
			if (data.IsNull || data.Value == null)
				data = new SqlBinary(new byte[] { });

			var sha = SHA384.Create();
			data = new SqlBinary(sha.ComputeHash(data.Value));
			sha.Clear();
			return data;
		}

		/// <summary>
		/// Sha512 Crypto
		/// </summary>
		/// <param name="data">data</param>
		/// <returns>Sha512</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("SHA512 单向哈希加密")]
		public static SqlBinary Sha512Crypto([Description("要加密的数据")]SqlBinary data)
		{
			if (data.IsNull || data.Value == null)
				data = new SqlBinary(new byte[] { });

			var sha = SHA512.Create();
			data = new SqlBinary(sha.ComputeHash(data.Value));
			sha.Clear();
			return data;
		}

		private static byte[] Crypto(RSACryptoServiceProvider rsa, byte[] data, int buffer, bool isCrypto)
		{
			var l = data.Length;
			if (buffer <= 0)
			{
				buffer = rsa.KeySize / 8;
				if (isCrypto)
					buffer -= 11;
			}
			if (l > buffer)
			{
				using (var ms = new MemoryStream())
				{
					for (var i = 0; i < l; i += buffer)
					{
						var rs = new byte[l - i - buffer > 0 ? buffer : l - i];
						Array.Copy(data, i, rs, 0, rs.Length);
						if (isCrypto)
							rs = rsa.Encrypt(rs, false);
						else
							rs = rsa.Decrypt(rs, false);
						ms.Write(rs, 0, rs.Length);
					}
					data = ms.ToArray();
					ms.Close();
				}
				return data;
			}
			if (isCrypto)
				data = rsa.Encrypt(data, false);
			else
				data = rsa.Decrypt(data, false);
			return data;
		}

		/// <summary>
		/// 公钥 RSA 加密
		/// </summary>
		/// <param name="data">要加密的数据</param>
		/// <param name="key">公钥</param>
		/// <param name="buffer">分段加密的数据长度，为将来保留</param>
		/// <returns>已加密的数据</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("RSA 不对称加密，公钥加密，私钥解密")]
		public static SqlBinary RsaEncrypt([Description("要加密的数据")]SqlBinary data, [Description("加密公钥")]SqlBinary key, [Description("分段加密长度，可为空")]SqlInt32 buffer)
		{
			if (data.IsNull)
				return data;

			if (key.IsNull)
				return data;

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
				return data;

			var pk = key.Value;
			if (pk == null || pk.Length == 0)
				return data;

			using (var rsa = new RSACryptoServiceProvider())
			{
				rsa.ImportCspBlob(pk);
				bs = Security.Crypto(rsa, bs, buffer.IsNull ? 0 : buffer.Value, true);
			}
			data = bs;
			return data;
		}

		/// <summary>
		/// 私钥 RSA 解密
		/// </summary>
		/// <param name="data">要解密的数据</param>
		/// <param name="key">私钥</param>
		/// <param name="buffer">分段加密的数据长度，为将来保留</param>
		/// <returns>已解密的数据</returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("RSA 不对称解密，公钥加密，私钥解密")]
		public static SqlBinary RsaDecrypt([Description("要解密的数据")]SqlBinary data, [Description("解密私钥")]SqlBinary key, [Description("分段解密长度，可为空")]SqlInt32 buffer)
		{
			if (data.IsNull)
				return data;

			if (key.IsNull)
				return data;

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
				return data;

			var sk = key.Value;
			if (sk == null || sk.Length == 0)
				return data;

			using (var rsa = new RSACryptoServiceProvider())
			{
				rsa.ImportCspBlob(sk);
				bs = Security.Crypto(rsa, bs, buffer.IsNull ? 0 : buffer.Value, false);
			}
			data = bs;
			return data;
		}

		/// <summary>
		/// RSA 加解密及获取公私钥。
		/// </summary>
		/// <param name="data">要加解密的数据</param>
		/// <param name="key">非空为加密</param>
		/// <param name="secret">非空为解密</param>
		/// <param name="buffer">分段加密的数据长度，为将来保留</param>
		/// <returns>操作结果</returns>
		[SqlProcedure]
		[Description("RSA 不对称加解密，公钥加密，私钥解密")]
		[return: Description("返回零表示取公私密钥对，正数表示加密，负数表示解密")]
		public static int RsaCrypto([Description("要加解密的数据，空表示生成公私密钥对")]ref SqlBinary data, [Description("加密公钥，空表示解密")]ref SqlBinary key, [Description("解密私钥，空表示加密")]ref SqlBinary secret, [Description("分段加解密长度，可为空")]SqlInt32 buffer)
		{
			var rsa = new RSACryptoServiceProvider();
			if (data.IsNull)
			{
				if (key.IsNull)
					key = rsa.ExportCspBlob(false);
				if (secret.IsNull)
					secret = rsa.ExportCspBlob(true);
				rsa.Clear();
				return 0;
			}

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
			{
				if (key.IsNull)
					key = rsa.ExportCspBlob(false);
				if (secret.IsNull)
					secret = rsa.ExportCspBlob(true);
				rsa.Clear();
				return 0;
			}

			if (key.IsNull)
			{
				if (secret.IsNull)//加密
				{
					key = rsa.ExportCspBlob(false);
					secret = rsa.ExportCspBlob(true);
					bs = Security.Crypto(rsa, bs, buffer.IsNull ? 0 : buffer.Value, true);//加密
					data = bs;
					rsa.Clear();
					return 1;
				}

				rsa.ImportCspBlob(secret.Value);
				key = rsa.ExportCspBlob(false);
				//iv = rsa.ToXmlString(true);
				bs = Security.Crypto(rsa, bs, buffer.IsNull ? 0 : buffer.Value, false);//解密
				data = bs;
				rsa.Clear();
				return -1;
			}

			rsa.ImportCspBlob(key.Value);
			bs = Security.Crypto(rsa, bs, buffer.IsNull ? 0 : buffer.Value, true);//加密
			data = bs;
			rsa.Clear();
			return 2;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="data"></param>
		/// <param name="key"></param>
		/// <param name="offset"></param>
		/// <param name="count"></param>
		/// <returns></returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("DSA 不对称签名，私钥签名，公钥验证")]
		public static SqlBinary DsaSignData([Description("要签名的数据")]SqlBinary data, [Description("签名私钥")]SqlBinary key, [Description("签名起始位置，可为空")]SqlInt32 offset, [Description("签名结束位置，可为空")]SqlInt32 count)
		{
			if (data.IsNull)
				return data;

			if (key.IsNull)
				return data;

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
				return data;

			var pk = key.Value;
			if (pk == null || pk.Length == 0)
				return data;

			var o = offset.IsNull ? 0 : offset.Value;
			var c = count.IsNull ? (bs.Length - o) : count.Value;
			var rsa = new DSACryptoServiceProvider();
			rsa.ImportCspBlob(pk);
			bs = rsa.SignData(bs, o, c);
			data = bs;
			return data;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="data"></param>
		/// <param name="key"></param>
		/// <param name="oid"></param>
		/// <returns></returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("DSA 不对称哈希签名，私钥签名，公钥验证")]
		public static SqlBinary DsaSignHash([Description("要签名的数据")]SqlBinary data, [Description("签名私钥")]SqlBinary key, [Description("签名哈希算法，可为空")]SqlString oid)
		{
			if (data.IsNull)
				return data;

			if (key.IsNull)
				return data;

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
				return data;

			var pk = key.Value;
			if (pk == null || pk.Length == 0)
				return data;

			var rsa = new DSACryptoServiceProvider();
			rsa.ImportCspBlob(pk);
			bs = rsa.SignHash(bs, (oid.IsNull || string.IsNullOrEmpty(oid.Value)) ? null : CryptoConfig.MapNameToOID(oid.Value));
			data = bs;
			return data;
		}

		/// <summary/>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("DSA 不对称验证，私钥签名，公钥验证")]
		[return: Description("返回空表示验证失败，否则返回原输入的要验证数据")]
		public static SqlBinary DsaVerifyData([Description("要验证的数据")]SqlBinary data, [Description("签名公钥")]SqlBinary key, [Description("签名起始位置，可为空")]SqlInt32 offset, [Description("签名结束位置，可为空")]SqlInt32 count, [Description("签名数据")]SqlBinary signature)
		{
			if (data.IsNull)
				return SqlBinary.Null;

			if (key.IsNull)
				return SqlBinary.Null;

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
				return SqlBinary.Null;

			var sk = key.Value;
			if (sk == null || sk.Length == 0)
				return SqlBinary.Null;

			if (signature.IsNull || signature.Value == null || signature.Value.Length == 0)
				return SqlBinary.Null;

			var rsa = new DSACryptoServiceProvider();
			rsa.ImportCspBlob(sk);
			var o = offset.IsNull ? 0 : offset.Value;
			var c = count.IsNull ? (bs.Length - o) : count.Value;
			if (bs.Length > c)
			{
				var vs = new byte[c];
				Array.Copy(bs, o, vs, 0, c);
				bs = vs;
			}
			if (rsa.VerifyData(bs, signature.Value))
				return data;

			return SqlBinary.Null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="data"></param>
		/// <param name="key"></param>
		/// <param name="oid"></param>
		/// <param name="signature"></param>
		/// <returns></returns>
		[SqlFunction(IsDeterministic = true, IsPrecise = false)]
		[Description("DSA 不对称哈希验证，私钥签名，公钥验证")]
		[return: Description("返回空表示验证失败，否则返回原输入的要验证数据")]
		public static SqlBinary DsaVerifyHash([Description("要验证的数据")]SqlBinary data, [Description("签名公钥")]SqlBinary key, [Description("签名哈希算法，可为空")]SqlString oid, [Description("签名数据")]SqlBinary signature)
		{
			if (data.IsNull)
				return SqlBinary.Null;

			if (key.IsNull)
				return SqlBinary.Null;

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
				return SqlBinary.Null;

			var sk = key.Value;
			if (sk == null || sk.Length == 0)
				return SqlBinary.Null;

			if (signature.IsNull || signature.Value == null || signature.Value.Length == 0)
				return SqlBinary.Null;

			var rsa = new DSACryptoServiceProvider();
			rsa.ImportCspBlob(sk);
			if (rsa.VerifyHash(bs, (oid.IsNull || string.IsNullOrEmpty(oid.Value)) ? null : CryptoConfig.MapNameToOID(oid.Value), signature.Value))
				return @data;

			return SqlBinary.Null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="data"></param>
		/// <param name="offset"></param>
		/// <param name="count"></param>
		/// <param name="signature"></param>
		/// <param name="key"></param>
		/// <param name="secret"></param>
		/// <returns></returns>
		[SqlProcedure]
		[Description("DSA 不对称签名与验证，私钥签名，公钥验证")]
		[return: Description("返回零表示取公私密钥对或验证成功，正数表示签名，负数表示验证失败")]
		public static int DsaSign([Description("要签名或验证的数据")]SqlBinary data, [Description("签名起始位置，可为空")]SqlInt32 offset, [Description("签名结束位置，可为空")]SqlInt32 count, [Description("签名数据")]ref SqlBinary signature, [Description("签名私钥，为空表示验证")]ref SqlBinary key, [Description("验证公钥，为空表示签名")]ref SqlBinary secret)
		{
			var dsa = new DSACryptoServiceProvider();
			if (data.IsNull)
			{
				if (key.IsNull)
					key = dsa.ExportCspBlob(false);
				if (secret.IsNull)
					secret = dsa.ExportCspBlob(true);
				return 0;
			}

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
			{
				if (key.IsNull)
					key = dsa.ExportCspBlob(false);
				if (secret.IsNull)
					secret = dsa.ExportCspBlob(true);
				return 0;
			}


			var o = offset.IsNull ? 0 : offset.Value;
			var c = count.IsNull ? (bs.Length - o) : count.Value;
			if (signature.IsNull || (key.IsNull && secret.IsNull))//加密
			{
				if (secret.IsNull)
					secret = dsa.ExportCspBlob(true);
				else
					dsa.ImportCspBlob(secret.Value);
				if (key.IsNull)
					key = dsa.ExportCspBlob(false);
				bs = dsa.SignData(bs, o, c);//加密
				signature = bs;
				return 1;
			}

			if (secret.IsNull)
				dsa.ImportCspBlob(key.Value);
			else
				dsa.ImportCspBlob(secret.Value);
			if (key.IsNull)
				key = dsa.ExportCspBlob(false);
			if (bs.Length > c)
			{
				var vs = new byte[c];
				Array.Copy(bs, o, vs, 0, c);
				bs = vs;
			}
			if (dsa.VerifyData(bs, signature.Value))//解密
				return 0;

			signature = SqlBinary.Null;
			return -1;
		}

		/// <summary>
		/// DSA 加解密及获取公私钥。
		/// </summary>
		/// <param name="data"></param>
		/// <param name="oid"></param>
		/// <param name="signature"></param>
		/// <param name="key"></param>
		/// <param name="secret"></param>
		/// <returns></returns>
		[SqlProcedure]
		[Description("DSA 不对称哈希签名与验证，私钥签名，公钥验证")]
		[return: Description("返回零表示取公私密钥对或验证成功，正数表示签名，负数表示验证失败")]
		public static int DsaHash([Description("要签名或验证的数据")]SqlBinary data, [Description("签名哈希算法，可为空")]SqlString oid, [Description("签名数据")]ref SqlBinary signature, [Description("签名私钥，为空表示验证")]ref SqlBinary key, [Description("验证公钥，为空表示签名")]ref SqlBinary secret)
		{
			var dsa = new DSACryptoServiceProvider();
			if (data.IsNull)
			{
				if (key.IsNull)
					key = dsa.ExportCspBlob(false);
				if (secret.IsNull)
					secret = dsa.ExportCspBlob(true);
				return 0;
			}

			var bs = data.Value;
			if (bs == null || bs.Length == 0)
			{
				if (key.IsNull)
					key = dsa.ExportCspBlob(false);
				if (secret.IsNull)
					secret = dsa.ExportCspBlob(true);
				return 0;
			}

			if (signature.IsNull || (key.IsNull && secret.IsNull))//加密
			{
				if (secret.IsNull)
					secret = dsa.ExportCspBlob(true);
				else
					dsa.ImportCspBlob(secret.Value);
				if (key.IsNull)
					key = dsa.ExportCspBlob(false);
				bs = dsa.SignHash(bs, (oid.IsNull || string.IsNullOrEmpty(oid.Value)) ? null : CryptoConfig.MapNameToOID(oid.Value));//加密
				signature = bs;
				return 1;
			}

			if (secret.IsNull)
				dsa.ImportCspBlob(key.Value);
			else
				dsa.ImportCspBlob(secret.Value);
			if (key.IsNull)
				key = dsa.ExportCspBlob(false);
			if (dsa.VerifyHash(bs, (oid.IsNull || string.IsNullOrEmpty(oid.Value)) ? null : CryptoConfig.MapNameToOID(oid.Value), signature.Value))//解密
				return 0;

			return -1;
		}
	}
}
